package com.john.test.fictiongetter;

import com.john.http.core.util.HtmlFetchUtil;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;

/**
 * @author jiangguangtao on 2016/9/20.
 */
public class DefaultFictionGetter {
    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultFictionGetter.class);
    private static final char[] specChars = new char[]{12288, 160};
    private FictionGetterConfig config;

    public DefaultFictionGetter(FictionGetterConfig config) {
        this.config = config;
    }

    /**
     * 处理并获取输出的指定流
     */
    public void process(OutputStream outputStream, Charset charset) {
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream, charset));
        try {
            boolean runFlag = true;

            String pageUri = config.getBeginUrl();
            while (runFlag) {
                FictionPager fp = getPager(pageUri);
                if (!StringUtils.isBlank(fp.getContent())) {
                    appendTxt(writer, fp);
                }
                if (fp.isEnd()) {
                    runFlag = false;
                }
                pageUri = fp.getNextUrl();
            }
        } catch (IOException e) {
            LOGGER.error("输出错误了", e);
        } finally {
            try {
                writer.flush();
                writer.close();
            } catch (IOException e) {

            }
        }
    }

    /**
     * 获取一页内容
     *
     * @param url
     * @return
     * @throws IOException
     */
    private FictionPager getPager(String url) throws IOException {
        FictionPager fp = null;
        LOGGER.info("");
        LOGGER.info("当前页：{}", url);
        Document document = getDocument(url, 1, 100);
        if(null == document) {
            LOGGER.warn("未获取到内容");
            return new FictionPager(true);
        }
        String title = null;
        if(TitleTypeEnum.TITLE.equals(config.getTitleType())) {
            title = trimTitle(document.title());
            LOGGER.info("标题：{}", title);
        } else if(TitleTypeEnum.SELECTOR.equals(config.getTitleType())) {
            Element titleElement = document.select(config.getTitleSelector()).first();
            if (null == titleElement) {
                LOGGER.warn("通过路径选择符：{}未查询到标题内容", config.getTitleSelector());
                return new FictionPager(true);
            }
            title = trimTitle(titleElement.text());
            LOGGER.info("标题：{}", title);
        }


        Element content = document.select(config.getContentSelector()).first();
        if (null == content) {
            LOGGER.warn("未发文档内容");
            return new FictionPager(true);
        }

        String ftext = content.text();
        LOGGER.debug("当前页文档内容!\n{}", ftext);
        fp = new FictionPager(title, ftext);

        Element nextPageLink = document.select(config.getNextPageSelector()).first();;
        if (null == nextPageLink) {
            LOGGER.error("未发现下一页");
            fp.setEnd(true);
            return fp;
        }

        String nextLink = nextPageLink.attr("abs:href");
        LOGGER.info("下一页是：{}", nextLink);
        if (StringUtils.equalsIgnoreCase(config.getEndUrl(), nextLink)) {
            LOGGER.info("结束获取");
            fp.setEnd(true);
        }

        fp.setNextUrl(nextLink);
        return fp;
    }
    /**
     * 追加内容到输出流中
     *
     * @param writer
     * @param fp
     * @throws IOException
     */
    private void appendTxt(BufferedWriter writer, FictionPager fp) throws IOException {
        boolean processFirstLineTitle = false;
        if(StringUtils.isNotBlank(fp.getTitle())) {
            writer.write(fp.getTitle());
            writer.newLine();
            processFirstLineTitle = true;
        }
        String[] lines = fp.getContent().split(config.getParagraphSep());
        for (String str : lines) {
            if(processFirstLineTitle) {
                processFirstLineTitle = false;
                if(config.getTitleMatcherPattern().matcher(str).find()) {
                    LOGGER.debug("skip title content in body :{}", str);
                    continue;
                }
            }
            writer.write(trimLine(str));
            writer.newLine();
        }
        writer.newLine();
        writer.flush();
    }

    /**
     * 清除行中的多余空格
     * @param str
     * @return
     */
    private String trimLine(String str) {
        if(StringUtils.isBlank(str)) {
            return StringUtils.EMPTY;
        }
        for(char scr : specChars) {
            str = str.replace(scr, ' ');
        }
        str = str.replaceFirst("\\s+", " ");
        return str;
    }

    /**
     * 获取一页内容，如果没有主体则重试 重试4次
     * @param url
     * @param times
     * @param delay
     * @return
     * @throws IOException
     */
    private Document getDocument(String url, int times, long delay) throws IOException {
        if (times > 1) {
            LOGGER.info("第{}次获取:{}", times, url);
        }
//        Document document = Jsoup.connect(url).get();
        String rawHtml = HtmlFetchUtil.getHtml(url, config.getCharset());
        if(StringUtils.isEmpty(rawHtml)) {
            return null;
        }
        Document document = Jsoup.parse(rawHtml, url);

        if (delay > 1) {
            try {
                Thread.sleep(delay);
            } catch (InterruptedException e) {
            }
        }

        Element content = document.select(config.getContentSelector()).first();
        if (null == content) {
            return null;
        } else {
            return document;
        }
    }

    /**
     * 处理标题
     *
     * @param title
     * @return
     */
    private String trimTitle(String title) {
        if (StringUtils.isNotBlank(config.getTitlePreRemove())) {
            title = StringUtils.removeStart(title, config.getTitlePreRemove());
        }
        if (StringUtils.isNotBlank(config.getTitlePostRemove())) {
            title = StringUtils.removeEnd(title, config.getTitlePostRemove());
        }
        return StringUtils.trimToEmpty(title);
    }
}
